package com.kexio.enterprise.governance;

import com.kexio.enterprise.governance.circuitbreaker.CircuitBreakerService;
import com.kexio.enterprise.governance.circuitbreaker.config.CircuitBreakerConfig;
import com.kexio.enterprise.governance.circuitbreaker.enums.CircuitBreakerState;
import com.kexio.enterprise.governance.circuitbreaker.listener.CircuitBreakerStateChangeListener;
import com.kexio.enterprise.governance.circuitbreaker.result.CircuitBreakerResult;
import com.kexio.enterprise.governance.circuitbreaker.statistics.CircuitBreakerStatistics;
import com.kexio.enterprise.governance.circuitbreaker.status.CircuitBreakerStatus;
import com.kexio.enterprise.governance.ratelimit.RateLimitService;
import com.kexio.enterprise.governance.ratelimit.config.RateLimitConfig;
import com.kexio.enterprise.governance.ratelimit.result.RateLimitResult;
import com.kexio.enterprise.governance.ratelimit.statistics.RateLimitStatistics;
import com.kexio.enterprise.governance.ratelimit.status.RateLimiterStatus;
import com.kexio.enterprise.governance.retry.RetryService;
import com.kexio.enterprise.governance.retry.config.RetryConfig;
import com.kexio.enterprise.governance.retry.enums.RetryStrategy;
import com.kexio.enterprise.governance.retry.result.RetryAttempt;
import com.kexio.enterprise.governance.retry.result.RetryResult;
import com.kexio.enterprise.governance.retry.statistics.RetryStatistics;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import jakarta.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;

/**
 * 治理模块自动配置
 * 
 * 功能说明：
 * - 限流服务：多种算法的API限流
 * - 熔断服务：服务熔断和降级
 * - 重试服务：智能重试机制
 * 
 * @author Kexio
 * @since 1.0.0
 */
@Configuration
@ConditionalOnProperty(name = "kexio.enterprise.governance.enabled", havingValue = "true", matchIfMissing = true)
public class GovernanceAutoConfiguration {

    private static final Logger log = LoggerFactory.getLogger(GovernanceAutoConfiguration.class);

    @PostConstruct
    public void init() {
        log.info("==> Kexio治理模块已启用");
        log.debug("治理模块包含: 限流服务、熔断服务、重试服务");
    }

    /**
     * 限流服务
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(name = "kexio.enterprise.governance.ratelimit.enabled", havingValue = "true", matchIfMissing = true)
    public RateLimitService rateLimitService() {
        log.debug("==> 创建RateLimitService实例");
        return new DefaultRateLimitService();
    }

    /**
     * 熔断服务
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(name = "kexio.enterprise.governance.circuitbreaker.enabled", havingValue = "true", matchIfMissing = true)
    public CircuitBreakerService circuitBreakerService() {
        log.debug("==> 创建CircuitBreakerService实例");
        return new DefaultCircuitBreakerService();
    }

    /**
     * 重试服务
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(name = "kexio.enterprise.governance.retry.enabled", havingValue = "true", matchIfMissing = true)
    public RetryService retryService() {
        log.debug("==> 创建RetryService实例");
        return new DefaultRetryService();
    }

    /**
     * 默认限流服务实现（简化版）
     */
    private static class DefaultRateLimitService implements RateLimitService {
        private static final Logger log = LoggerFactory.getLogger(DefaultRateLimitService.class);
        private final Map<String, RateLimitConfig> configs = new ConcurrentHashMap<>();
        private final Map<String, RateLimitStatistics> statistics = new ConcurrentHashMap<>();

        @Override
        public RateLimitResult tryAcquire(String key) {
            return tryAcquire(key, 1);
        }

        @Override
        public RateLimitResult tryAcquire(String key, int tokens) {
            RateLimitResult result = new RateLimitResult(true);
            result.setAvailableTokens(100); // 模拟剩余令牌
            
            // 更新统计
            updateStatistics(key, true);
            
            log.debug("限流检查模拟: key={}, tokens={}, allowed={}", key, tokens, result.isAllowed());
            return result;
        }

        @Override
        public RateLimitResult tryAcquire(String key, RateLimitConfig config) {
            setConfig(key, config);
            return tryAcquire(key, 1);
        }

        @Override
        public RateLimitResult acquire(String key) {
            return tryAcquire(key);
        }

        @Override
        public RateLimitResult acquire(String key, int tokens) {
            return tryAcquire(key, tokens);
        }

        @Override
        public RateLimitResult acquire(String key, int tokens, long timeoutMs) {
            return tryAcquire(key, tokens);
        }

        @Override
        public long getAvailableTokens(String key) {
            return 100; // 模拟可用令牌数
        }

        @Override
        public void reset(String key) {
            log.debug("重置限流器模拟: {}", key);
        }

        @Override
        public void batchReset(List<String> keys) {
            log.debug("批量重置限流器模拟: {} 个", keys.size());
        }

        @Override
        public void setConfig(String key, RateLimitConfig config) {
            configs.put(key, config);
            log.debug("设置限流配置模拟: {}", key);
        }

        @Override
        public RateLimitConfig getConfig(String key) {
            return configs.getOrDefault(key, new RateLimitConfig());
        }

        @Override
        public void remove(String key) {
            configs.remove(key);
            statistics.remove(key);
            log.debug("删除限流器模拟: {}", key);
        }

        @Override
        public java.util.Set<String> getAllKeys() {
            return configs.keySet();
        }

        @Override
        public RateLimitStatistics getStatistics(String key) {
            return statistics.getOrDefault(key, new RateLimitStatistics());
        }

        @Override
        public Map<String, RateLimitStatistics> getAllStatistics() {
            return new java.util.HashMap<>(statistics);
        }

        @Override
        public void clearStatistics(String key) {
            statistics.remove(key);
            log.debug("清空限流统计模拟: {}", key);
        }

        @Override
        public boolean exists(String key) {
            return configs.containsKey(key);
        }

        @Override
        public void warmup(String key, int tokens) {
            log.debug("预热限流器模拟: {} tokens={}", key, tokens);
        }

        @Override
        public RateLimiterStatus getStatus(String key) {
            RateLimiterStatus status = new RateLimiterStatus();
            status.setKey(key);
            status.setConfig(getConfig(key));
            status.setActive(true);
            status.setCurrentTokens(100);
            status.setCreateTime(LocalDateTime.now());
            return status;
        }

        private void updateStatistics(String key, boolean allowed) {
            RateLimitStatistics stats = statistics.computeIfAbsent(key, k -> {
                RateLimitStatistics s = new RateLimitStatistics();
                s.setKey(k);
                s.setStartTime(LocalDateTime.now());
                return s;
            });
            
            stats.setTotalRequests(stats.getTotalRequests() + 1);
            if (allowed) {
                stats.setAllowedRequests(stats.getAllowedRequests() + 1);
            } else {
                stats.setRejectedRequests(stats.getRejectedRequests() + 1);
            }
            stats.setLastRequestTime(LocalDateTime.now());
            
            long total = stats.getTotalRequests();
            if (total > 0) {
                stats.setAllowRate((double) stats.getAllowedRequests() / total);
            }
        }
    }

    /**
     * 默认熔断服务实现（简化版）
     */
    private static class DefaultCircuitBreakerService implements CircuitBreakerService {
        private static final Logger log = LoggerFactory.getLogger(DefaultCircuitBreakerService.class);
        private final Map<String, CircuitBreakerConfig> configs = new ConcurrentHashMap<>();
        private final Map<String, CircuitBreakerState> states = new ConcurrentHashMap<>();
        private final Map<String, CircuitBreakerStatistics> statistics = new ConcurrentHashMap<>();

        @Override
        public <T> CircuitBreakerResult<T> execute(String key, Supplier<T> supplier) {
            return execute(key, supplier, () -> null);
        }

        @Override
        public <T> CircuitBreakerResult<T> execute(String key, Supplier<T> supplier, Supplier<T> fallback) {
            CircuitBreakerState state = states.getOrDefault(key, CircuitBreakerState.CLOSED);
            
            if (state == CircuitBreakerState.OPEN) {
                // 熔断器打开，执行降级
                T fallbackResult = fallback != null ? fallback.get() : null;
                CircuitBreakerResult<T> result = new CircuitBreakerResult<>(fallbackResult);
                result.setState(state);
                result.setFromFallback(true);
                result.setReason("熔断器已打开");
                
                updateStatistics(key, false, 0);
                log.debug("熔断器已打开，执行降级: {}", key);
                return result;
            }
            
            try {
                long startTime = System.currentTimeMillis();
                T result = supplier.get();
                long executionTime = System.currentTimeMillis() - startTime;
                
                recordSuccess(key, executionTime);
                
                CircuitBreakerResult<T> cbResult = new CircuitBreakerResult<>(result);
                cbResult.setState(state);
                cbResult.setExecutionTime(executionTime);
                
                log.debug("熔断器执行成功: {} time={}ms", key, executionTime);
                return cbResult;
                
            } catch (Exception e) {
                recordFailure(key, e);
                
                CircuitBreakerResult<T> result = new CircuitBreakerResult<>(e);
                result.setState(getState(key));
                
                log.debug("熔断器执行失败: {} error={}", key, e.getMessage());
                return result;
            }
        }

        @Override
        public <T> CircuitBreakerResult<T> execute(String key, Supplier<T> supplier, CircuitBreakerConfig config) {
            setConfig(key, config);
            return execute(key, supplier);
        }

        @Override
        public <T> CompletableFuture<CircuitBreakerResult<T>> executeAsync(String key, Supplier<T> supplier) {
            return CompletableFuture.supplyAsync(() -> execute(key, supplier));
        }

        @Override
        public <T> CompletableFuture<CircuitBreakerResult<T>> executeAsync(String key, Supplier<T> supplier, Supplier<T> fallback) {
            return CompletableFuture.supplyAsync(() -> execute(key, supplier, fallback));
        }

        @Override
        public boolean isExecutionAllowed(String key) {
            CircuitBreakerState state = states.getOrDefault(key, CircuitBreakerState.CLOSED);
            return state != CircuitBreakerState.OPEN;
        }

        @Override
        public void recordSuccess(String key, long executionTime) {
            updateStatistics(key, true, executionTime);
            // 简化：成功后重置为关闭状态
            states.put(key, CircuitBreakerState.CLOSED);
        }

        @Override
        public void recordFailure(String key, Throwable exception) {
            updateStatistics(key, false, 0);
            // 简化：失败后可能打开熔断器
            CircuitBreakerStatistics stats = getStatistics(key);
            if (stats.getFailedCalls() > 5) { // 简单阈值
                states.put(key, CircuitBreakerState.OPEN);
                log.warn("熔断器已打开: {} 失败次数: {}", key, stats.getFailedCalls());
            }
        }

        @Override
        public void recordTimeout(String key) {
            recordFailure(key, new java.util.concurrent.TimeoutException("执行超时"));
        }

        @Override
        public void forceOpen(String key) {
            states.put(key, CircuitBreakerState.FORCED_OPEN);
            log.info("强制打开熔断器: {}", key);
        }

        @Override
        public void forceClose(String key) {
            states.put(key, CircuitBreakerState.FORCED_CLOSED);
            log.info("强制关闭熔断器: {}", key);
        }

        @Override
        public void reset(String key) {
            states.put(key, CircuitBreakerState.CLOSED);
            statistics.remove(key);
            log.info("重置熔断器: {}", key);
        }

        @Override
        public CircuitBreakerState getState(String key) {
            return states.getOrDefault(key, CircuitBreakerState.CLOSED);
        }

        @Override
        public CircuitBreakerStatus getStatus(String key) {
            CircuitBreakerStatus status = new CircuitBreakerStatus();
            status.setKey(key);
            status.setState(getState(key));
            status.setConfig(getConfig(key));
            
            CircuitBreakerStatistics stats = getStatistics(key);
            status.setNumberOfSuccessfulCalls(stats.getSuccessfulCalls());
            status.setNumberOfFailedCalls(stats.getFailedCalls());
            status.setFailureRate(stats.getFailureRate());
            
            return status;
        }

        @Override
        public void setConfig(String key, CircuitBreakerConfig config) {
            configs.put(key, config);
            log.debug("设置熔断器配置模拟: {}", key);
        }

        @Override
        public CircuitBreakerConfig getConfig(String key) {
            return configs.getOrDefault(key, new CircuitBreakerConfig());
        }

        @Override
        public void remove(String key) {
            configs.remove(key);
            states.remove(key);
            statistics.remove(key);
            log.debug("删除熔断器模拟: {}", key);
        }

        @Override
        public java.util.Set<String> getAllKeys() {
            return configs.keySet();
        }

        @Override
        public CircuitBreakerStatistics getStatistics(String key) {
            return statistics.computeIfAbsent(key, k -> {
                CircuitBreakerStatistics stats = new CircuitBreakerStatistics();
                stats.setKey(k);
                stats.setStartTime(LocalDateTime.now());
                return stats;
            });
        }

        @Override
        public Map<String, CircuitBreakerStatistics> getAllStatistics() {
            return new java.util.HashMap<>(statistics);
        }

        @Override
        public void clearStatistics(String key) {
            statistics.remove(key);
            log.debug("清空熔断器统计模拟: {}", key);
        }

        @Override
        public void addStateChangeListener(String key, CircuitBreakerStateChangeListener listener) {
            log.debug("添加状态变化监听器模拟: {}", key);
        }

        @Override
        public void removeStateChangeListener(String key, CircuitBreakerStateChangeListener listener) {
            log.debug("移除状态变化监听器模拟: {}", key);
        }

        @Override
        public boolean exists(String key) {
            return configs.containsKey(key);
        }

        private void updateStatistics(String key, boolean success, long executionTime) {
            CircuitBreakerStatistics stats = getStatistics(key);
            
            stats.setTotalCalls(stats.getTotalCalls() + 1);
            if (success) {
                stats.setSuccessfulCalls(stats.getSuccessfulCalls() + 1);
            } else {
                stats.setFailedCalls(stats.getFailedCalls() + 1);
            }
            
            stats.setLastCallTime(LocalDateTime.now());
            
            // 更新执行时间统计
            if (executionTime > 0) {
                long totalTime = (long) (stats.getAverageExecutionTime() * (stats.getTotalCalls() - 1)) + executionTime;
                stats.setAverageExecutionTime((double) totalTime / stats.getTotalCalls());
                
                if (executionTime > stats.getMaxExecutionTime()) {
                    stats.setMaxExecutionTime(executionTime);
                }
                if (stats.getMinExecutionTime() == 0 || executionTime < stats.getMinExecutionTime()) {
                    stats.setMinExecutionTime(executionTime);
                }
            }
            
            // 计算失败率
            if (stats.getTotalCalls() > 0) {
                stats.setFailureRate((double) stats.getFailedCalls() / stats.getTotalCalls());
                stats.setSuccessRate((double) stats.getSuccessfulCalls() / stats.getTotalCalls());
            }
        }
    }

    /**
     * 默认重试服务实现（简化版）
     */
    private static class DefaultRetryService implements RetryService {
        private static final Logger log = LoggerFactory.getLogger(DefaultRetryService.class);
        private final Map<String, RetryConfig> configs = new ConcurrentHashMap<>();
        private final Map<String, RetryStatistics> statistics = new ConcurrentHashMap<>();

        @Override
        public <T> RetryResult<T> execute(String key, Supplier<T> supplier) {
            return execute(key, supplier, getConfig(key));
        }

        @Override
        public <T> RetryResult<T> execute(String key, Supplier<T> supplier, RetryConfig config) {
            setConfig(key, config);
            
            RetryResult<T> result = new RetryResult<>(false);
            result.setStartTime(LocalDateTime.now());
            
            for (int attempt = 1; attempt <= config.getMaxAttempts(); attempt++) {
                RetryAttempt attemptRecord = new RetryAttempt(attempt);
                long startTime = System.currentTimeMillis();
                
                try {
                    T value = supplier.get();
                    long executionTime = System.currentTimeMillis() - startTime;
                    
                    attemptRecord.setSuccess(true);
                    attemptRecord.setExecutionTime(executionTime);
                    result.getAttempts().add(attemptRecord);
                    
                    result.setSuccess(true);
                    result.setResult(value);
                    result.setAttemptCount(attempt);
                    result.setEndTime(LocalDateTime.now());
                    
                    updateStatistics(key, true, attempt, executionTime);
                    log.debug("重试执行成功: {} attempt={}", key, attempt);
                    return result;
                    
                } catch (Exception e) {
                    long executionTime = System.currentTimeMillis() - startTime;
                    
                    attemptRecord.setSuccess(false);
                    attemptRecord.setExecutionTime(executionTime);
                    attemptRecord.setException(e);
                    result.getAttempts().add(attemptRecord);
                    
                    result.setLastException(e);
                    result.setAttemptCount(attempt);
                    
                    if (attempt < config.getMaxAttempts()) {
                        // 计算等待时间
                        long waitTime = calculateWaitTime(config, attempt);
                        attemptRecord.setWaitTime(waitTime);
                        
                        try {
                            Thread.sleep(waitTime);
                        } catch (InterruptedException ie) {
                            Thread.currentThread().interrupt();
                            break;
                        }
                        
                        log.debug("重试执行失败，等待重试: {} attempt={} wait={}ms", key, attempt, waitTime);
                    } else {
                        log.debug("重试执行最终失败: {} attempts={}", key, attempt);
                    }
                }
            }
            
            result.setEndTime(LocalDateTime.now());
            result.setTotalExecutionTime(
                java.time.Duration.between(result.getStartTime(), result.getEndTime()).toMillis()
            );
            
            updateStatistics(key, false, result.getAttemptCount(), result.getTotalExecutionTime());
            return result;
        }

        @Override
        public <T> RetryResult<T> execute(String key, Supplier<T> supplier, Supplier<T> fallback) {
            RetryResult<T> result = execute(key, supplier);
            if (!result.isSuccess() && fallback != null) {
                try {
                    T fallbackResult = fallback.get();
                    result.setResult(fallbackResult);
                    result.setFromFallback(true);
                    result.setSuccess(true);
                    log.debug("重试失败，执行降级: {}", key);
                } catch (Exception e) {
                    log.error("降级执行也失败: {}", key, e);
                }
            }
            return result;
        }

        @Override
        public <T> CompletableFuture<RetryResult<T>> executeAsync(String key, Supplier<T> supplier) {
            return CompletableFuture.supplyAsync(() -> execute(key, supplier));
        }

        @Override
        public <T> CompletableFuture<RetryResult<T>> executeAsync(String key, Supplier<T> supplier, RetryConfig config) {
            return CompletableFuture.supplyAsync(() -> execute(key, supplier, config));
        }

        @Override
        public RetryResult<Void> execute(String key, Runnable runnable) {
            return execute(key, () -> {
                runnable.run();
                return null;
            });
        }

        @Override
        public RetryResult<Void> execute(String key, Runnable runnable, RetryConfig config) {
            return execute(key, () -> {
                runnable.run();
                return null;
            }, config);
        }

        @Override
        public void setConfig(String key, RetryConfig config) {
            configs.put(key, config);
            log.debug("设置重试配置模拟: {}", key);
        }

        @Override
        public RetryConfig getConfig(String key) {
            return configs.getOrDefault(key, new RetryConfig());
        }

        @Override
        public void remove(String key) {
            configs.remove(key);
            statistics.remove(key);
            log.debug("删除重试配置模拟: {}", key);
        }

        @Override
        public java.util.Set<String> getAllKeys() {
            return configs.keySet();
        }

        @Override
        public RetryStatistics getStatistics(String key) {
            return statistics.computeIfAbsent(key, k -> {
                RetryStatistics stats = new RetryStatistics();
                stats.setKey(k);
                stats.setStartTime(LocalDateTime.now());
                return stats;
            });
        }

        @Override
        public Map<String, RetryStatistics> getAllStatistics() {
            return new java.util.HashMap<>(statistics);
        }

        @Override
        public void clearStatistics(String key) {
            statistics.remove(key);
            log.debug("清空重试统计模拟: {}", key);
        }

        @Override
        public boolean exists(String key) {
            return configs.containsKey(key);
        }

        private long calculateWaitTime(RetryConfig config, int attempt) {
            long waitTime = config.getInitialIntervalMs();
            
            switch (config.getStrategy()) {
                case EXPONENTIAL_BACKOFF:
                    waitTime = (long) (config.getInitialIntervalMs() * Math.pow(config.getMultiplier(), attempt - 1));
                    break;
                case LINEAR_BACKOFF:
                    waitTime = config.getInitialIntervalMs() * attempt;
                    break;
                case RANDOM_INTERVAL:
                    waitTime = (long) (config.getInitialIntervalMs() * (1 + Math.random() * config.getRandomFactor()));
                    break;
                case CUSTOM_INTERVAL:
                    if (config.getCustomIntervals() != null && attempt <= config.getCustomIntervals().size()) {
                        waitTime = config.getCustomIntervals().get(attempt - 1);
                    }
                    break;
                case FIXED_INTERVAL:
                default:
                    waitTime = config.getInitialIntervalMs();
                    break;
            }
            
            // 应用抖动
            if (config.isEnableJitter()) {
                double jitter = (Math.random() - 0.5) * 2 * config.getRandomFactor();
                waitTime = (long) (waitTime * (1 + jitter));
            }
            
            // 限制最大等待时间
            return Math.min(waitTime, config.getMaxIntervalMs());
        }

        private void updateStatistics(String key, boolean success, int attempts, long executionTime) {
            RetryStatistics stats = getStatistics(key);
            
            stats.setTotalExecutions(stats.getTotalExecutions() + 1);
            if (success) {
                stats.setSuccessfulExecutions(stats.getSuccessfulExecutions() + 1);
            } else {
                stats.setFailedExecutions(stats.getFailedExecutions() + 1);
            }
            
            stats.setTotalRetries(stats.getTotalRetries() + (attempts - 1));
            stats.setLastExecutionTime(LocalDateTime.now());
            
            if (attempts > stats.getMaxAttempts()) {
                stats.setMaxAttempts(attempts);
            }
            
            // 更新平均尝试次数
            stats.setAverageAttempts((double) (stats.getTotalRetries() + stats.getTotalExecutions()) / stats.getTotalExecutions());
            
            // 更新成功率
            if (stats.getTotalExecutions() > 0) {
                stats.setSuccessRate((double) stats.getSuccessfulExecutions() / stats.getTotalExecutions());
            }
            
            // 更新执行时间统计
            if (executionTime > 0) {
                long totalTime = (long) (stats.getAverageExecutionTime() * (stats.getTotalExecutions() - 1)) + executionTime;
                stats.setAverageExecutionTime((double) totalTime / stats.getTotalExecutions());
                
                if (executionTime > stats.getMaxExecutionTime()) {
                    stats.setMaxExecutionTime(executionTime);
                }
                if (stats.getMinExecutionTime() == 0 || executionTime < stats.getMinExecutionTime()) {
                    stats.setMinExecutionTime(executionTime);
                }
            }
        }
    }
}
